package com.kly.manager.websocket;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.kly.domain.entity.User;
import com.kly.manager.websocket.enums.PictureCommentMessageTypeEnum;
import com.kly.manager.websocket.model.PictureCommentMessageRequest;
import com.kly.manager.websocket.model.PictureCommentMessageResponse;
import com.kly.service.PictureCommentService;
import com.kly.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;


@Configuration
@Slf4j
public class PictureCommentHandler extends TextWebSocketHandler {


    @Resource
    private UserService userService;

    @Resource
    private PictureCommentService pictureCommentService;

    /**
     * pictureId 下的评论用户websocket session
     */
    private final Map<Long, Set<WebSocketSession>> commentSessionMap = new ConcurrentHashMap<>();


    /**
     * 广播信息给pictureId下的所有评论用户
     */
    public void broadcastToPicture(Long pictureId, PictureCommentMessageResponse messageResponse, WebSocketSession excludeSession) throws IOException {
    //     获取pictureId下的评论用户websocket session
        Set<WebSocketSession> webSocketSessions = commentSessionMap.get(pictureId);
        if (CollUtil.isEmpty(webSocketSessions)) {
            return;
        }
    //     处理精度损失问题 id Long 转换为 String
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addSerializer(Long.class, ToStringSerializer.instance);
        module.addSerializer(Long.TYPE, ToStringSerializer.instance);
        objectMapper.registerModule(module);
        String message = objectMapper.writeValueAsString(messageResponse);
        // 封装消息
        TextMessage textMessage = new TextMessage(message);
        for (WebSocketSession webSocketSession : webSocketSessions) {
            if (webSocketSession.isOpen() && !webSocketSession.equals(excludeSession)) {
                webSocketSession.sendMessage(textMessage);
            }
        }
    }

    /**
     * 广播信息给pictureId下的所有用户
     * @param pictureId
     * @param messageResponse
     * @throws IOException
     */
    public void broadcastToPicture(Long pictureId, PictureCommentMessageResponse messageResponse) throws IOException {
        broadcastToPicture(pictureId, messageResponse, null);
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    //     获取pictureId 和 user信息
        Long pictureId = (long) session.getAttributes().get("pictureId");
        User loginUser = (User) session.getAttributes().get("loginUser");
        // pictureId 不存在则创建一个线程安全集合Set
        commentSessionMap.putIfAbsent(pictureId, ConcurrentHashMap.newKeySet());
        commentSessionMap.get(pictureId).add(session);

    //     封装消息
        PictureCommentMessageResponse pictureCommentMessageResponse = new PictureCommentMessageResponse();
        pictureCommentMessageResponse.setType(PictureCommentMessageTypeEnum.INFO.getValue());
        String message = String.format("用户%s加入页面", loginUser.getUserName());
        pictureCommentMessageResponse.setMessage(message);
        pictureCommentMessageResponse.setUserVO(userService.getUserVo(loginUser));
        broadcastToPicture(pictureId, pictureCommentMessageResponse);
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
    //     转换为PictureCommentMessageRequest
        PictureCommentMessageRequest pictureCommentMessageRequest = JSONUtil.toBean(message.getPayload(), PictureCommentMessageRequest.class);
    //     获取发送信息
        String comment = pictureCommentMessageRequest.getContent();
        if (comment == null) {
            return;
        }
    //     发送消息
        Long pictureId = (long) session.getAttributes().get("pictureId");
        User loginUser = (User) session.getAttributes().get("loginUser");
        PictureCommentMessageResponse pictureCommentMessageResponse = new PictureCommentMessageResponse();
        pictureCommentMessageResponse.setType(PictureCommentMessageTypeEnum.COMMENT_ACTION.getValue());
        pictureCommentMessageResponse.setContent(comment);
        pictureCommentMessageResponse.setUserVO(userService.getUserVo(loginUser));
        String messageInfo = String.format("用户%s评论了%s", loginUser.getUserName(), pictureId);
        pictureCommentMessageResponse.setMessage(messageInfo);
        broadcastToPicture(pictureId, pictureCommentMessageResponse, session);

    //     存储评论信息
        pictureCommentService.addComment(pictureId, comment, loginUser);

    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        Long pictureId = (long) session.getAttributes().get("pictureId");
        User loginUser = (User) session.getAttributes().get("loginUser");
        Set<WebSocketSession> webSocketSessions = commentSessionMap.get(pictureId);
        if (CollUtil.isNotEmpty(webSocketSessions)) {
            webSocketSessions.remove(session);
            if(CollUtil.isEmpty(webSocketSessions)) {
                commentSessionMap.remove(pictureId);
            }
        }

    //     发送消息
        PictureCommentMessageResponse pictureCommentMessageResponse = new PictureCommentMessageResponse();
        pictureCommentMessageResponse.setType(PictureCommentMessageTypeEnum.INFO.getValue());
        String message = String.format("用户%s退出页面", loginUser.getUserName());
        pictureCommentMessageResponse.setMessage(message);
        pictureCommentMessageResponse.setUserVO(userService.getUserVo(loginUser));
        broadcastToPicture(pictureId, pictureCommentMessageResponse);
    }
}